﻿Imports System.Data.SqlClient
Imports System.Security.Cryptography
Imports System.Web.Configuration
Imports GuildCMS.Common

Namespace Account
    Public Class Reset
        Inherits System.Web.UI.Page

#Region " Database variables "

        ''' <summary>
        ''' Set the database connection variables used throughout this class.
        ''' </summary>
        ''' <remarks>Placed here so they will not need to be typed out each time they are needed.</remarks>
        Private connectionString As String = WebConfigurationManager.ConnectionStrings("SqlConnectionString").ConnectionString()
        Private connection As SqlConnection = New SqlConnection(connectionString)
        Private command As New SqlCommand

#End Region

#Region " Page events "

        ''' <summary>
        ''' Handles the Load event of the Page control.
        ''' </summary>
        ''' <param name="sender">The source of the event.</param>
        ''' <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            ' Add "equalTo" value to the txtPassword2 text field to enable jQuery validate match checking.
            txtPassword2.Attributes.Add("equalTo", "#" & txtPassword1.ClientID)

            ' Set the minimum length for the password for jQuery Validate plugin.
            txtPassword1.Attributes.Add("MinLength", CInt(GetSetting("PasswordLength")))
            txtPassword2.Attributes.Add("MinLength", CInt(GetSetting("PasswordLength")))

            ' Populate the email text box using the on supplied in the query string if available.
            If Not Request.QueryString("email") Is Nothing Then
                txtEmail.Text = Request.QueryString("email")
            End If

            ' Populate the authorization code text box using the one supplied in the query string if available.
            If Not Request.QueryString("code") Is Nothing Then
                txtCode.Text = Request.QueryString("code")
            End If
        End Sub

#End Region

#Region " Click event for when the reset form is submitted. "

        ''' <summary>
        ''' Handles the Click event of the btnReset control.
        ''' </summary>
        ''' <param name="sender">The source of the event.</param>
        ''' <param name="e">The <see cref="System.EventArgs" /> instance containing the event data.</param>
        Protected Sub btnReset_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnReset.Click
            If Page.IsValid Then
                ' Get the MD5 salt from the database.
                Dim salt As String = CStr(GetSetting("PasswordSalt"))
                Try
                    ' Insert the new password and authentication code into the database
                    command = New SqlCommand("sp_public_changePassword", connection)
                    command.CommandType = CommandType.StoredProcedure
                    command.Parameters.Add("@Email", SqlDbType.NVarChar, 100)
                    command.Parameters("@Email").Value = txtEmail.Text
                    command.Parameters.Add("@Password", SqlDbType.NChar, 32)
                    command.Parameters("@Password").Value = HashPassword(txtPassword1.Text, salt)
                    command.Parameters.Add("@Code", SqlDbType.NChar, 10)
                    command.Parameters("@Code").Value = MakeRandomString(10)
                    connection.Open()
                    command.ExecuteNonQuery()
                    connection.Close()
                Catch ex As Exception

                End Try

                ' Hide the form and display success message.
                panForm.Visible = False
                panComplete.Visible = True
            Else
                ' Something contained in the form failed validation so display the form again.
                panForm.Visible = True
                panComplete.Visible = False
            End If
        End Sub

#End Region

#Region " Form OnServerValidate functions. "

        ''' <summary>
        ''' Handles the ServerValidate event of the valEmail control.
        ''' </summary>
        ''' <param name="source">The source of the event.</param>
        ''' <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs" /> instance containing the event data.</param>
        Protected Sub valEmail_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            ' Check that a vaild email was entered.
            args.IsValid = True

            ' Check that a valid e-mail was entered using a regular expression.
            Dim emailRegex As New Regex("^([0-9a-zA-Z]([-\.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$")
            If txtEmail.Text = "" Or emailRegex.IsMatch(txtEmail.Text) = False Then
                args.IsValid = False
            End If

            ' Check the length of the email address supplied.
            If txtEmail.Text.Length() > 100 Then
                args.IsValid = False
            End If

            ' Check that the supplied email address exist within the database.
            Try
                command = New SqlCommand("sp_public_countEmails", connection)
                command.CommandType = CommandType.StoredProcedure
                command.Parameters.Add("@Email", SqlDbType.NVarChar, 100)
                command.Parameters("@Email").Value = txtEmail.Text
                command.Parameters.Add("@Total", SqlDbType.Int)
                command.Parameters("@Total").Direction = ParameterDirection.Output
                connection.Open()
                command.ExecuteNonQuery()
                Dim totalRows As Integer = CInt(command.Parameters("@Total").Value)
                connection.Close()
                If totalRows < 1 Then
                    args.IsValid = False
                End If
            Catch ex As Exception
                ' Any exception will cause args.IsValid to equal False.
                args.IsValid = False
            End Try
        End Sub

        ''' <summary>
        ''' Handles the ServerValidate event of the valCode control.
        ''' </summary>
        ''' <param name="source">The source of the event.</param>
        ''' <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs" /> instance containing the event data.</param>
        Protected Sub valCode_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            ' Set args.IsValid to True by default.
            args.IsValid = True

            ' Check that the supplied code is alphanumeric.
            Dim loginRegex As New Regex("[a-zA-Z0-9]")
            If txtCode.Text = "" Or loginRegex.IsMatch(txtCode.Text) = False Then
                args.IsValid = False
            End If

            ' Check that the length of the code supplied is acceptable.
            If Not txtCode.Text.Length() = 10 Then
                args.IsValid = False
            End If
        End Sub

        ''' <summary>
        ''' Handles the ServerValidate event of the valPassword1 control.
        ''' </summary>
        ''' <param name="source">The source of the event.</param>
        ''' <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs" /> instance containing the event data.</param>
        Protected Sub valPassword1_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            ' Check the length of the password supplied in the txtPassword1 text box.
            If txtPassword1.Text.Length() >= CInt(GetSetting("PasswordLength")) And txtPassword1.Text.Length() <= 32 Then
                ' If the length is between the correct parameters the check passed.
                args.IsValid = True
            Else
                ' The length is out of parameters so validation fails.
                args.IsValid = False
            End If
        End Sub

        ''' <summary>
        ''' Handles the ServerValidate event of the valPassword2 control.
        ''' </summary>
        ''' <param name="source">The source of the event.</param>
        ''' <param name="args">The <see cref="System.Web.UI.WebControls.ServerValidateEventArgs" /> instance containing the event data.</param>
        Protected Sub valPassword2_ServerValidate(ByVal source As Object, ByVal args As ServerValidateEventArgs)
            'Check that the password supplied in txtPassword1 is the same as the one supplied in txtPassword2.
            If txtPassword1.Text = txtPassword2.Text Then
                args.IsValid = True
            Else
                args.IsValid = False
            End If
        End Sub

#End Region

#Region " Data generation specific functions. "

        ''' <summary>
        ''' Hashes the password supplied via the form.
        ''' </summary>
        ''' <param name="password">The password supplied via the form.</param>
        ''' <param name="salt">The salt used when generating the MD5 hash.</param>
        ''' <returns></returns>
        Private Function HashPassword(ByVal password As String, ByVal salt As String) As String
            ' Create an MD5 hash of the supplied password.
            Dim sourceText As String = salt + password
            Dim asciiEnc As ASCIIEncoding = Nothing
            Dim hash As String = ""
            Dim byteSourceText() As Byte = asciiEnc.GetBytes(sourceText)
            Dim md5Hash As New MD5CryptoServiceProvider
            Dim byteHash() As Byte = md5Hash.ComputeHash(byteSourceText)
            For Each b As Byte In byteHash
                hash &= b.ToString("x2")
            Next

            ' Return the hashed password
            Return hash
        End Function

        ''' <summary>
        ''' Generates a random activation code.
        ''' </summary>
        ''' <param name="size">The length we want the string to be.</param>
        ''' <returns>Returns the randomly generated activation code user to activate the new account.</returns>
        Private Function MakeRandomString(ByVal size As Integer) As String
            Dim rand As New Random()
            ' Characters we will use to generate this random string.
            Dim allowableChars() As Char = "ABCDEFGHIJKLOMNOPQRSTUVWXYZ0123456789".ToCharArray()

            ' Start generating the random string.
            Dim activationCode As String = String.Empty
            For i As Integer = 0 To size - 1
                activationCode += allowableChars(rand.Next(allowableChars.Length - 1))
            Next

            ' Return the random string in upper case.
            Return activationCode.ToUpper()
        End Function

#End Region

    End Class
End Namespace